#include <ws2tcpip.h>
#include <process.h>
#include "TCPServer.h"
#include "TCPConnection.h"
#include "../../Utils/Errors.h"
//
struct TCPHadlerThreadArg {
	SOCKET _sck;
	TCPServer* _server;
	
	TCPHadlerThreadArg(SOCKET sck, TCPServer* server) : _sck(sck), _server(server) 
	{}
};
//
TCPServer::TCPServer(unsigned short port, ITCPHandler* handler) : _port(port), _handler(handler), _listenSocket(0) {
	//
}
//
TCPServer::~TCPServer() {
	if (_listenSocket != 0) {
		closesocket(_listenSocket);
	}
}
//
bool TCPServer::start() {
	WSADATA wsd;
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
		showErrorMessage();
        return false;
    }
	//
	memset(&_sa, 0, sizeof(_sa));
	_sa.sin_family = AF_INET;
	_sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	_sa.sin_port = htons(_port);
	//
	_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (bind(_listenSocket, (SOCKADDR*) &_sa, sizeof(_sa)) != 0) {
		showErrorMessage();
		closesocket(_listenSocket);
		return false;
	}
	//
	int size =(int)sizeof(_sa);
    if(getsockname(_listenSocket, (SOCKADDR*)&_sa, &size) == SOCKET_ERROR) {
        showErrorMessage();
		closesocket(_listenSocket);
        return false;
    }
	//
	_beginthread(_thrProcessConnections, 0, (void*)this);
	return true;
}
//
void TCPServer::stop() {
}
//
unsigned short TCPServer::getPort() {
	return ntohs(_sa.sin_port);
}
//
void __cdecl TCPServer::_thrProcessConnections(void *arg) {
	TCPServer* _this = (TCPServer*) arg;
	_this->_processConections();
}
//
void TCPServer::_processConections() {
	while(listen(_listenSocket, 0) == 0) {
		int addrlen = (int)sizeof(_sa);
		SOCKET sck = accept(_listenSocket, (sockaddr*)&_sa, &addrlen);
		_beginthread(_thrConnectionHandler, 0, (void*)new TCPHadlerThreadArg(sck, this));
	}
}
//
void __cdecl TCPServer::_thrConnectionHandler(void *arg) {
	TCPHadlerThreadArg* hta = (TCPHadlerThreadArg*) arg;
	TCPConnection* connection = new TCPConnection(hta->_sck);
	hta->_server->_handler->processConnection(connection);
	delete connection;
	delete hta;
}
//